﻿/**************************************************************************
创建日期:	2019/7/19
作	  者:	张存
邮 	  箱:	zhangcunliang@126.com
创建时间:	
Copyright (c)  ZhCun.cn

描	述：  webAPi或web页面的登录验证过滤器
记	录： 将webapi与webpage 统一使用一个过滤器
         2019.9.26 去除缓存验证菜单的角色权限配置，（运行正常后删除注释）

***************************************************************************/
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using ZhCun.WebCommon.AL;
using ZhCun.WebCommon.ApiModels;
using ZhCun.WebCommon.DBModels;

namespace ZhCun.WebCommon
{
    public class LoginAuthorizeAttribute : Attribute, IAuthorizationFilter
    {
        /// <summary>
        /// 是否webapi
        /// </summary>
        public bool IsWebApi { set; get; } = true;

        /// <summary>
        /// 验证操作
        /// </summary>
        /// <param name="context"></param>
        /// <param name="tokenObj"></param>
        protected bool VerifyLogin(AuthorizationFilterContext context, TSysLoginToken tokenObj)
        {
            bool isAjax = IsWebApi || context.HttpContext.Request.IsAjax();

            if (tokenObj == null)
            {
                //token验证失败
                if (isAjax)
                {
                    context.Result = new ObjectResult("login verification fail") { StatusCode = StatusCodes.Status401Unauthorized };
                }
                else
                {
                    context.HttpContext.Response.Redirect(AppCommonConfig.AppSettings.LoginPageUrl);
                    context.Result = new EmptyResult();
                }
                return false;
            }
            else
            {
                LoginStateCache.ResetExpire(tokenObj);

                //ajax请求与页面请求都增加缓存的过期时间
                DateTime newExpiresTime = DateTime.Now.AddMinutes(tokenObj.LoginExpiresMinute);
                if (tokenObj.LoginExpiresTime < DateTime.Now.AddMinutes(tokenObj.LoginExpiresMinute / 2))
                {
                    //过期时间小于一半时间的时候进行更新数据库的token过期时间
                    // 过期时间（11:30） < 当前时间（11:00） + 过期分钟数（60） / 2
                    new ALUserBase().RefreshTokenAsync(tokenObj.LoginToken, newExpiresTime);
                }
                tokenObj.LoginExpiresTime = newExpiresTime;
                LoginStateCache.Set(tokenObj);

                context.HttpContext.SetCookie(Constants.TOKEN_NAME, tokenObj.LoginToken, newExpiresTime);

                return true;
            }
        }

        //protected bool VerifyRole(AuthorizationFilterContext context, TSysLoginToken tokenObj)
        //{
        //    //token验证通过
        //    if (!UrlRightsCache.IsHasRights(tokenObj.RoleId, context.HttpContext.Request.Path.Value))
        //    {
        //        // url 权限验证失败
        //        context.Result = new ContentResult
        //        {
        //            StatusCode = StatusCodes.Status403Forbidden,
        //            Content = "error code 403，权限限制"
        //        };
        //        return false;
        //    }
        //    return true;
        //}


        /// <summary>
        /// 是否包含为 不验证
        /// </summary>
        private bool IsNonLoginVerify(MemberInfo mi)
        {
            return mi.GetCustomAttributes(typeof(NonLoginVerifyAttribute), false).Any();
        }

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            // 如果不是一个控制器方法则直接返回
            if (!(context.ActionDescriptor is ControllerActionDescriptor action))
            {
                return;
            }

            //如果Controller加了[NonAuthorize] 跳过权限验证
            if (IsNonLoginVerify(action.ControllerTypeInfo))
            {
                return;
            }

            //如果Action加了[NonAuthorize] 跳过权限检查
            if (IsNonLoginVerify(action.MethodInfo))
            {
                return;
            }

            var tokenObj = context.HttpContext.GetTokenObject();
            if (VerifyLogin(context, tokenObj) == false) return;

            if (AppCommonConfig.AppSettings.ManageRoleId.Equals(tokenObj.RoleId, StringComparison.CurrentCultureIgnoreCase))
            {
                //如果当前登录角色为管理员角色，则跳过角色权限判断
                return;
            }

            if (action.MethodInfo.GetCustomAttributes(typeof(NonRoleVerifyAttribute), true).Any() ||
                action.ControllerTypeInfo.GetCustomAttributes(typeof(NonRoleVerifyAttribute), true).Any())
            {
                //如果包含不验证权限的标注，则不进行角色权限验证
                return;
            }

            if (!(action.MethodInfo.GetCustomAttributes(typeof(RoleVerifyAttribute), true).FirstOrDefault() is RoleVerifyAttribute roleAttr))
            {
                roleAttr = action.ControllerTypeInfo.GetCustomAttributes(typeof(RoleVerifyAttribute), true).FirstOrDefault() as RoleVerifyAttribute;
            }

            string[] rightsRoles;
            if (roleAttr == null || roleAttr.RoleIds == null || roleAttr.RoleIds.Length == 0)
            {
                rightsRoles = AppCommonConfig.AppSettings.DefaultRoleIds.Split(',');
            }
            else
            {
                rightsRoles = roleAttr.RoleIds;
            }

            if (rightsRoles != null && rightsRoles.Length > 0)
            {
                foreach (var roleId in rightsRoles)
                {
                    if (tokenObj.RoleId.Equals(roleId, StringComparison.CurrentCultureIgnoreCase))
                    {
                        return;
                    }
                }
            }

            // url 权限验证失败，没有权限验证的都视为错误
            context.Result = new ContentResult
            {
                StatusCode = StatusCodes.Status403Forbidden,
                Content = "error code 403，权限限制"
            };

            //2019.9.26 去除白名单（缓存）模式，改用标注特性
            //VerifyRole(context, tokenObj);
        }
    }
}